package com.channel.connector.ctripos.service.impl;

import com.alibaba.fastjson.JSON;
import com.channel.connector.ctripos.common.constant.CtripOSCommonConstants;
import com.channel.connector.ctripos.common.constant.CtripOSCommonUtil;
import com.channel.connector.ctripos.common.constant.InitData;
import com.channel.connector.ctripos.common.util.DateUtil;
import com.channel.connector.ctripos.domain.CtriposMapHotelDO;
import com.channel.connector.ctripos.domain.CtriposMapRateplanDO;
import com.channel.connector.ctripos.domain.CtriposMapRoomtypeDO;
import com.channel.connector.ctripos.domain.CtriposShopInfoDO;
import com.channel.connector.ctripos.dto.ResponseDTO;
import com.channel.connector.ctripos.dto.request.CommonIncrementDTO;
import com.channel.connector.ctripos.dto.request.IncrementDTO;
import com.channel.connector.ctripos.dto.request.IncrementType;
import com.channel.connector.ctripos.enums.BreakFastTypeEnum;
import com.channel.connector.ctripos.enums.CancelPenaltyTypeEnum;
import com.channel.connector.ctripos.enums.CurrencyEnum;
import com.channel.connector.ctripos.enums.ErrorCodeEnum;
import com.channel.connector.ctripos.enums.RestrictionTypeEnum;
import com.channel.connector.ctripos.mapper.CtriposMapHotelMapper;
import com.channel.connector.ctripos.mapper.CtriposMapRateplanMapper;
import com.channel.connector.ctripos.mapper.CtriposMapRoomtypeMapper;
import com.channel.connector.ctripos.service.CtripOSRemoteInvokeService;
import com.channel.connector.ctripos.service.CtriposMappingService;
import com.channel.connector.ctripos.service.CtriposProductService;
import com.channel.connector.ctripos.webservice.dynamic.AmountPercentType;
import com.channel.connector.ctripos.webservice.dynamic.ArrayOfCancelPenaltyType;
import com.channel.connector.ctripos.webservice.dynamic.ArrayOfRateAmountMessageTypeRate;
import com.channel.connector.ctripos.webservice.dynamic.ArrayOfRateUploadTypeBaseByGuestAmt;
import com.channel.connector.ctripos.webservice.dynamic.AvailStatusMessageType;
import com.channel.connector.ctripos.webservice.dynamic.AvailabilityStatusType;
import com.channel.connector.ctripos.webservice.dynamic.CancelPenaltyType;
import com.channel.connector.ctripos.webservice.dynamic.CancelPenaltyTypeDeadline;
import com.channel.connector.ctripos.webservice.dynamic.CompanyNameType;
import com.channel.connector.ctripos.webservice.dynamic.LengthsOfStayType;
import com.channel.connector.ctripos.webservice.dynamic.OTAHotelAvailNotifRQ;
import com.channel.connector.ctripos.webservice.dynamic.OTAHotelRateAmountNotifRQ;
import com.channel.connector.ctripos.webservice.dynamic.POSType;
import com.channel.connector.ctripos.webservice.dynamic.RateAmountMessageType;
import com.channel.connector.ctripos.webservice.dynamic.RateUploadType;
import com.channel.connector.ctripos.webservice.dynamic.SourceType;
import com.channel.connector.ctripos.webservice.dynamic.StatusApplicationControlType;
import com.channel.connector.ctripos.webservice.dynamic.TimeUnitType;
import com.channel.connector.product.domain.PriceAndQuotaDO;
import com.channel.connector.product.service.PricePlanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: zhanwang
 * @create: 2019-03-12 10:23
 **/
@Service
@Slf4j
public class CtriposProductServiceImpl implements CtriposProductService {
    @Autowired
    private CtripOSRemoteInvokeService ctripOSRemoteInvokeService;

    @Autowired
    private CtriposMappingService ctriposMappingService;

    @Autowired
    private PricePlanService pricePlanService;

    @Autowired
    private CtriposMapHotelMapper hotelMapper;

    @Autowired
    private CtriposMapRoomtypeMapper roomtypeMapper;

    @Autowired
    private CtriposMapRateplanMapper rateplanMapper;

    @Override
    public ResponseDTO push(CommonIncrementDTO commonIncrementDTO) {

        ResponseDTO responseDTO = new ResponseDTO(ErrorCodeEnum.SUCCESS);
        if (null == commonIncrementDTO || StringUtils.isBlank(commonIncrementDTO.getMerchantCode())) {
            log.error("1增量推送，参数校验不通过。{}", JSON.toJSONString(commonIncrementDTO));
            responseDTO.setErrorCode(ErrorCodeEnum.INVALID_INPUTPARAM);
            return responseDTO;
        }

        /**
         * 1-获取店铺信息：根据merchantCode
         * 2-校验是否有映射关系，没有映射的直接抛弃不推送增量。注意：校验时要带上shopId，因为多店铺的时候，可能某些酒店没上这个店铺。
         * 3-根据增量类型调用对应的
         */
        String merchantCode = commonIncrementDTO.getMerchantCode();
        IncrementType incrementType = commonIncrementDTO.getIncrementType();

        List<CtriposShopInfoDO> shopInfoDTOList = InitData.merchantShopMap.get(merchantCode);


        CommonIncrementDTO pushRequestDTO;
        //分店铺推送:这样shopId只需要在最外层传入即可；
        for (CtriposShopInfoDO shopInfoDTO : shopInfoDTOList) {
            // 此处不仅仅过滤映射，还要把已映射的信息保存，传递到后面去，这样后续的方法中不需要再次查询映射了
            List<IncrementDTO> filteredList = mappingFilter(commonIncrementDTO.getIncrementDTOList(), merchantCode, shopInfoDTO.getId().toString());
            pushRequestDTO = new CommonIncrementDTO();
            pushRequestDTO.setShopId(shopInfoDTO.getId().toString());
            pushRequestDTO.setIncrementType(commonIncrementDTO.getIncrementType());
            pushRequestDTO.setMerchantCode(commonIncrementDTO.getMerchantCode());
            pushRequestDTO.setIncrementDTOList(filteredList);

            //分类型推送

            // 1. 暂不考虑价格计划增量推送
            // 2. 价格或房态增量推送
            if (incrementType.isPrice() || incrementType.isRoomStatus()) {
                log.info("进入价格和房态增量分之：{}", JSON.toJSONString(pushRequestDTO));
                responseDTO = pushPriceAndRoomStatus(pushRequestDTO);
            }
            // 3. 上下架增量：携程没有上下架接口，上下架会产生价格、房态增量，所以不需要重复推送
        }

        return responseDTO;
    }

    @Override
    public ResponseDTO pushPriceAndRoomStatus(CommonIncrementDTO commonIncrementDTO) {
        ResponseDTO responseDTO = new ResponseDTO(ErrorCodeEnum.SUCCESS);

        /**
         * 1、查询价格和房态：多店铺时，价格是要根据不同的渠道查询的。因为目前设计的是多店铺使用多渠道的方式。
         * 渠道信息可以从shopInfo中获取，（shopId和merchantCode就能确定一个channelCode）
         * 2、调用携程海外接口
         */
        if (!validateParams(commonIncrementDTO)) {
            log.error("推送价格和房态失败：参数校验不通过。{}", JSON.toJSONString(commonIncrementDTO));
            responseDTO.setErrorCode(ErrorCodeEnum.INVALID_INPUTPARAM);
            return responseDTO;
        }

        String shopId = commonIncrementDTO.getShopId();
        String merchantCode = commonIncrementDTO.getMerchantCode();
        CtriposShopInfoDO shopInfoDO = InitData.shopIdShopMap.get(shopId);

        //过滤调未上架的数据，只同步已上架的数据
        List<IncrementDTO> incrementDTOList = commonIncrementDTO.getIncrementDTOList();

        //查询一个价格计划所有要推送的日期的价格、房态、条款
        Map<Long, List<PriceAndQuotaDO>> priceAndQuotaMap = new HashMap<>();
        for (IncrementDTO incrementDTO : incrementDTOList) {
            // 查询价格、配额
            if (incrementDTO.getStartDate() == null || incrementDTO.getEndDate() == null) {
                //默认推送60天的数据过去
                incrementDTO.setStartDate(DateUtil.dateToString(DateUtil.getCurrentDate()));
                incrementDTO.setEndDate(DateUtil.dateToString(DateUtil.getDate(DateUtil.getCurrentDate(), 60, 0)));
            }
            List<PriceAndQuotaDO> priceAndQuotaDOList = pricePlanService.queryPriceAndQuota(incrementDTO, merchantCode);
            priceAndQuotaMap.put(incrementDTO.getMRatePlanId(), priceAndQuotaDOList);
        }


        // 1. 推送房态到携程
        List<OTAHotelAvailNotifRQ> availNotifRQS = convert2RoomStatusIncrements(priceAndQuotaMap, shopInfoDO, incrementDTOList);
        if (CollectionUtils.isNotEmpty(availNotifRQS)) {
            for (OTAHotelAvailNotifRQ availNotifRQ : availNotifRQS) {
                //推送房态,每次只更新一个商品的
                ctripOSRemoteInvokeService.pushAvailabilityAndInventory(availNotifRQ, commonIncrementDTO.getShopId());
            }
        }

        // 2. 推送价格到携程
        List<OTAHotelRateAmountNotifRQ> rateAmountNotifRQS = convert2PriceIncrements(priceAndQuotaMap, shopInfoDO, incrementDTOList);
        if (CollectionUtils.isNotEmpty(rateAmountNotifRQS)) {
            for (OTAHotelRateAmountNotifRQ rateAmountNotifRQ : rateAmountNotifRQS) {
                //推送房价, 每次只更新一个商品的
                this.ctripOSRemoteInvokeService.pushRate(rateAmountNotifRQ, commonIncrementDTO.getShopId());
            }
        }

        return responseDTO;
    }

    /**
     * 构建推送价格请求对象
     *
     * @param priceAndQuotaMap
     * @param shopInfoDO
     * @param incrementDTOList
     * @return
     */
    private List<OTAHotelRateAmountNotifRQ> convert2PriceIncrements(Map<Long, List<PriceAndQuotaDO>> priceAndQuotaMap, CtriposShopInfoDO shopInfoDO, List<IncrementDTO> incrementDTOList) {
        List<OTAHotelRateAmountNotifRQ> rateAmountNotifRQS = new ArrayList<>();
        for (IncrementDTO incrementDTO : incrementDTOList) {
            OTAHotelRateAmountNotifRQ rateRq = new OTAHotelRateAmountNotifRQ();
            //报价请求节点
            OTAHotelRateAmountNotifRQ.RateAmountMessages rateAmountMessages = new OTAHotelRateAmountNotifRQ.RateAmountMessages();
            rateAmountMessages.setHotelCode(String.valueOf(incrementDTO.getMHotelId()));
            rateAmountMessages.getRateAmountMessage().addAll(new ArrayList<>());

            //价格初始信息
            RateAmountMessageType rateAmountMessage = new RateAmountMessageType();
            StatusApplicationControlType statusApplicationControlType = new StatusApplicationControlType();
            statusApplicationControlType.setInvTypeCode(String.valueOf(incrementDTO.getMRoomTypeId()));
            statusApplicationControlType.setRatePlanCode(String.valueOf(incrementDTO.getMRatePlanId()));
            statusApplicationControlType.setRatePlanCategory(CtripOSCommonConstants.PAY_METHOD_PREPAY);
            rateAmountMessage.setStatusApplicationControl(statusApplicationControlType);
            ArrayOfRateAmountMessageTypeRate rates = new ArrayOfRateAmountMessageTypeRate();
            rates.getRate().addAll(new ArrayList<>());
            rateAmountMessage.setRates(rates);
            rateAmountMessages.getRateAmountMessage().add(rateAmountMessage);


            List<PriceAndQuotaDO> priceAndQuotaDOS = priceAndQuotaMap.get(incrementDTO.getMRatePlanId());
            for (PriceAndQuotaDO priceAndQuotaDO : priceAndQuotaDOS) {
                ArrayOfRateAmountMessageTypeRate.Rate rate = this.buildDefaultRate(priceAndQuotaDO.getSaleDate(), CurrencyEnum.CNY.value);
                rateAmountMessage.getRates().getRate().add(rate);

                // 无售价||无销售状态||无房态||无商品ID||无支付方式
                if (null == priceAndQuotaDO.getCtriposSalePrice()
                        || null == priceAndQuotaDO.getCtriposSaleState()
                        || null == priceAndQuotaDO.getQuotaState()
                        || null == priceAndQuotaDO.getPriceplanId()
                        || null == priceAndQuotaDO.getPayMethod()) {
                    log.debug("无售价||无销售状态||无房态||无商品ID||无支付方式, priceAndQuotaDO= {}", priceAndQuotaDO);
                    continue;
                }

                //组装价格
                //早餐, -1床位早默认数量为2，其他以早餐数为准
                if (null != priceAndQuotaDO.getBreakFastType() && 0 != priceAndQuotaDO.getBreakFastType()) {
                    rate.getMealsIncluded().setBreakfast(true);
                    if (priceAndQuotaDO.getBreakFastType() == BreakFastTypeEnum.BED.key) {
                        rate.getMealsIncluded().setNumberOfBreakfast(new BigInteger("2"));
                    } else {
                        rate.getMealsIncluded().setNumberOfBreakfast(new BigInteger("" + priceAndQuotaDO.getBreakFastType()));
                    }
                } else {
                    rate.getMealsIncluded().setBreakfast(false);
                }

                ArrayOfRateUploadTypeBaseByGuestAmt.BaseByGuestAmt baseByGuestAmt = rate.getBaseByGuestAmts().getBaseByGuestAmt().get(0);
                baseByGuestAmt.setAmountBeforeTax(priceAndQuotaDO.getCtriposSalePrice());
                baseByGuestAmt.setAmountAfterTax(priceAndQuotaDO.getCtriposSalePrice());
                baseByGuestAmt.setCurrencyCode(CurrencyEnum.CNY.value);
            }


            // 认证信息
            POSType posType = buildPosType(shopInfoDO);

            rateRq.setPOS(posType);
            rateRq.setRateAmountMessages(rateAmountMessages);
            rateRq.setTimeStamp(CtripOSCommonUtil.dateToXMLGregorianCalendar(new Date()));
            rateRq.setPrimaryLangID(CtripOSCommonConstants.CTRIP_OS_PRIMARY_LANG_ID_EN);
            rateRq.setVersion(CtripOSCommonConstants.CTRIP_OS_IF_VERSION);
            rateAmountNotifRQS.add(rateRq);
        }
        return rateAmountNotifRQS;
    }


    /**
     * 构建推送房态请求对象
     *
     * @param priceAndQuotaMap
     * @param shopInfoDO
     * @param incrementDTOList
     * @return
     */
    private List<OTAHotelAvailNotifRQ> convert2RoomStatusIncrements(Map<Long, List<PriceAndQuotaDO>> priceAndQuotaMap, CtriposShopInfoDO shopInfoDO, List<IncrementDTO> incrementDTOList) {
        List<OTAHotelAvailNotifRQ> availNotifRQS = new ArrayList<>();
        for (IncrementDTO incrementDTO : incrementDTOList) {
            OTAHotelAvailNotifRQ availRq = new OTAHotelAvailNotifRQ();
            //房态请求节点
            OTAHotelAvailNotifRQ.AvailStatusMessages availStatusMessages = new OTAHotelAvailNotifRQ.AvailStatusMessages();
            availStatusMessages.setHotelCode(String.valueOf(incrementDTO.getMHotelId()));
            List<AvailStatusMessageType> availStatusMessageTypes = new ArrayList<>();


            List<PriceAndQuotaDO> priceAndQuotaDOS = priceAndQuotaMap.get(incrementDTO.getMRatePlanId());
            for (PriceAndQuotaDO priceAndQuotaDO : priceAndQuotaDOS) {
                AvailStatusMessageType availStatusMessage = this.buildDefaultAvailStatusMessageType(incrementDTO.getMRatePlanId(), incrementDTO.getMRoomTypeId(), priceAndQuotaDO.getSaleDate());
                availStatusMessageTypes.add(availStatusMessage);

                // 无售价||无销售状态||无房态||无商品ID||无支付方式
                if (null == priceAndQuotaDO.getCtriposSalePrice()
                        || null == priceAndQuotaDO.getCtriposSaleState()
                        || null == priceAndQuotaDO.getQuotaState()
                        || null == priceAndQuotaDO.getPriceplanId()
                        || null == priceAndQuotaDO.getPayMethod()) {
                    log.debug("无售价||无销售状态||无房态||无商品ID||无支付方式, priceAndQuotaDO= {}", priceAndQuotaDO);
                    continue;
                }

                //组装房态
                if (null == priceAndQuotaDO.getCtriposSaleState()
                        || priceAndQuotaDO.getCtriposSaleState() == 0
                        || null == priceAndQuotaDO.getQuotaState()
                        || 0 == priceAndQuotaDO.getQuotaState()) {
                    //当天是下架状态，或者无房态情况
                    availStatusMessage.setBookingLimit(new BigInteger("0"));
                    availStatusMessage.getRestrictionStatus().setStatus(AvailabilityStatusType.CLOSE);
                } else if (priceAndQuotaDO.getCtriposSaleState() == 1) {
                    //有房
                    if (priceAndQuotaDO.getQuotaState() == 1) {
                        //有房，可超/不可超，配额>0
                        if (null != priceAndQuotaDO.getQuotaNum() && priceAndQuotaDO.getQuotaNum() > 0) {
                            availStatusMessage.getRestrictionStatus().setStatus(AvailabilityStatusType.OPEN);
                            availStatusMessage.setBookingLimit(new BigInteger(String.valueOf(priceAndQuotaDO.getQuotaNum())));
                        } else {
                            //是否可超
                            int overDraft = priceAndQuotaDO.getOverDraft() == null ? 0 : priceAndQuotaDO.getOverDraft();
                            //配额数
                            int quotaNum = priceAndQuotaDO.getQuotaNum() == null ? 0 : priceAndQuotaDO.getQuotaNum();
                            if (overDraft == 1 && quotaNum == 0) {
                                //有房，可超，配额=0
                                availStatusMessage.getRestrictionStatus().setStatus(AvailabilityStatusType.OPEN);
                                availStatusMessage.setBookingLimit(new BigInteger("0"));
                            } else if (overDraft == 0 && quotaNum == 0) {
                                //有房，不可超，配额=0
                                availStatusMessage.getRestrictionStatus().setStatus(AvailabilityStatusType.CLOSE);
                            }
                        }
                    }
                }


                //组装取消条款
                ArrayOfCancelPenaltyType arrayOfCancelPenaltyType = new ArrayOfCancelPenaltyType();
                CancelPenaltyType cancelPenaltyType = new CancelPenaltyType();
                CancelPenaltyTypeDeadline cancelPenaltyTypeDeadline = new CancelPenaltyTypeDeadline();
                cancelPenaltyTypeDeadline.setOffsetDropTime(CancelPenaltyTypeEnum.BEFOREARRIVAL.key);
                cancelPenaltyTypeDeadline.setOffsetTimeUnit(TimeUnitType.DAY);
                cancelPenaltyTypeDeadline.setOffsetUnitMultiplier(new BigInteger("23899"));
                AmountPercentType amountPercentType = new AmountPercentType();
                amountPercentType.setPercent(new BigDecimal("100"));
                cancelPenaltyType.setDeadline(cancelPenaltyTypeDeadline);
                cancelPenaltyType.setAmountPercent(amountPercentType);
                arrayOfCancelPenaltyType.getCancelPenalty().add(cancelPenaltyType);
                availStatusMessage.setTPAExtensions(arrayOfCancelPenaltyType);

                //组装入住条款
                if (null != priceAndQuotaDO.getOccupancyOfDays()) {
                    AvailStatusMessageType availStatusMessageLengthsOfStayType = this.buildCommonRoomStatus(incrementDTO.getMRatePlanId(), incrementDTO.getMRoomTypeId(), priceAndQuotaDO.getSaleDate());
                    availStatusMessageTypes.add(availStatusMessageLengthsOfStayType);

                    LengthsOfStayType lengthsOfStayType = new LengthsOfStayType();
                    List<LengthsOfStayType.LengthOfStay> lengthOfStayList = new ArrayList<>();
                    lengthsOfStayType.getLengthOfStay().addAll(lengthOfStayList);
                    LengthsOfStayType.LengthOfStay lengthOfStay = new LengthsOfStayType.LengthOfStay();
                    lengthOfStay.setTimeUnit(TimeUnitType.DAY);
                    //连住条款  推最小连住天数
                    lengthOfStay.setMinMaxMessageType("SetMinLOS");
                    lengthOfStay.setTime(new BigInteger(priceAndQuotaDO.getOccupancyOfDays() + ""));
                    lengthsOfStayType.getLengthOfStay().add(lengthOfStay);
                    availStatusMessageLengthsOfStayType.setLengthsOfStay(lengthsOfStayType);
                }
            }
            availStatusMessages.getAvailStatusMessage().addAll(availStatusMessageTypes);

            // 认证信息
            POSType posType = buildPosType(shopInfoDO);

            availRq.setPOS(posType);
            availRq.setAvailStatusMessages(availStatusMessages);
            availRq.setTimeStamp(CtripOSCommonUtil.dateToXMLGregorianCalendar(new Date()));
            availRq.setPrimaryLangID(CtripOSCommonConstants.CTRIP_OS_PRIMARY_LANG_ID_EN);
            availRq.setVersion(CtripOSCommonConstants.CTRIP_OS_IF_VERSION);
            availNotifRQS.add(availRq);
        }
        return availNotifRQS;
    }

    /**
     * @param commonIncrementDTO
     * @return true ：验证通过，false:验证不通过
     */
    private boolean validateParams(CommonIncrementDTO commonIncrementDTO) {
        boolean failed = (null == commonIncrementDTO || org.springframework.util.CollectionUtils.isEmpty(commonIncrementDTO.getIncrementDTOList()));
        if (failed) {
            return false;
        }
        CtriposShopInfoDO shopInfoDO = InitData.shopIdShopMap.get(commonIncrementDTO.getShopId());
        if (shopInfoDO == null) {
            return false;
        }
        return true;
    }

    /**
     * 没有映射的过滤掉
     *
     * @param incrementDTOList
     * @param merchantCode
     * @return
     */
    private List<IncrementDTO> mappingFilter(List<IncrementDTO> incrementDTOList, String merchantCode, String shopId) {

        if (org.springframework.util.CollectionUtils.isEmpty(incrementDTOList)) {
            log.error("判断映射结果为false，因为请求参数为空：{}", JSON.toJSONString(incrementDTOList));
            return incrementDTOList;
        }

        List<IncrementDTO> filteredList = new ArrayList<>();

        Map<Long, Boolean> hotelMappedMap = new HashMap<>();
        Map<Long, Boolean> roomTypeMappedMap = new HashMap<>();
        Map<Long, Boolean> ratePlanMappedMap = new HashMap<>();
        for (IncrementDTO incrementDTO : incrementDTOList) {

            Long hotelId = incrementDTO.getMHotelId();
            boolean isHotelMapped = isHotelMapped(hotelId, merchantCode, shopId, hotelMappedMap);

            //增量发送的地方必须有价格计划ID
            Long ratePlanId = incrementDTO.getMRatePlanId();
            boolean isRatePlanMapped = isRatePlanMapped(hotelId, ratePlanId, merchantCode, shopId, ratePlanMappedMap, incrementDTO);

            Long roomTypeId = incrementDTO.getMRoomTypeId();
            boolean isRoomTypeMapped = isRoomTypeMapped(hotelId, roomTypeId, merchantCode, shopId, roomTypeMappedMap);


            if (isAllMapped(isHotelMapped, isRoomTypeMapped, isRatePlanMapped)) {
                filteredList.add(incrementDTO);
            } else {
                log.info("酒店或者房型或者价格计划没有映射:isHotelMapped={},isRoomTypeMapped={},isRatePlanMapped={}，请求参数：{}"
                        , isHotelMapped, isRoomTypeMapped, isRatePlanMapped, JSON.toJSONString(incrementDTO));
            }
        }

        return filteredList;
    }

    /**
     * 酒店、房型、价格计划是否都已映射
     *
     * @param isHotelMapped
     * @param isRoomTypeMapped
     * @param isRatePlanMapped
     * @return
     */
    private boolean isAllMapped(boolean isHotelMapped, boolean isRoomTypeMapped, boolean isRatePlanMapped) {
        return isHotelMapped && isRoomTypeMapped && isRatePlanMapped;
    }

    /**
     * 价格计划是否映射
     *
     * @param hotelId
     * @param mRatePlanId
     * @param merchantCode
     * @param shopId
     * @param ratePlanMappedMap
     * @param incrementDTO
     * @return
     */
    private boolean isRatePlanMapped(Long hotelId, Long mRatePlanId, String merchantCode,
                                     String shopId, Map<Long, Boolean> ratePlanMappedMap, IncrementDTO incrementDTO) {

        boolean isMapped = false;

        if (null == hotelId || null == mRatePlanId || StringUtils.isBlank(merchantCode) || StringUtils.isBlank(shopId)) {
            log.error("判断价格计划映射时，酒店或者商家编码或者价格计划或者店铺为空。hotelId={}，mRatePlanId={}，merchantCode={}，shopId={}",
                    hotelId, mRatePlanId, merchantCode, shopId);
            return isMapped;
        }

        if (ratePlanMappedMap.containsKey(mRatePlanId)) {
            isMapped = ratePlanMappedMap.get(mRatePlanId);
        } else {

            CtriposMapRateplanDO rateplanDO = new CtriposMapRateplanDO();
            rateplanDO.setHotelId(hotelId.intValue());
            rateplanDO.setCommodityId(mRatePlanId.intValue());
            rateplanDO.setMerchantCode(merchantCode);
            rateplanDO.setShopId(Integer.valueOf(shopId));
            List<CtriposMapRateplanDO> rateplanDOS = rateplanMapper.select(rateplanDO);

            isMapped = !CollectionUtils.isEmpty(rateplanDOS) && rateplanDOS.get(0).getMapStatus() == 2;

            ratePlanMappedMap.put(mRatePlanId, isMapped);
            if (incrementDTO.getMRoomTypeId() == null) {
                incrementDTO.setMRoomTypeId(rateplanDOS.get(0).getRoomTypeId().longValue());
            }
        }

        return isMapped;
    }

    /**
     * 房型是否映射
     *
     * @param hotelId
     * @param mRoomTypeId
     * @param merchantCode
     * @param shopId
     * @param roomTypeMappedMap
     * @return
     */
    private boolean isRoomTypeMapped(Long hotelId, Long mRoomTypeId, String merchantCode, String shopId, Map<Long, Boolean> roomTypeMappedMap) {

        boolean isMapped = false;

        if (null == hotelId || null == mRoomTypeId || StringUtils.isBlank(merchantCode) || StringUtils.isBlank(shopId)) {
            log.error("判断房型映射时，酒店或者商家编码为空。");
            return isMapped;
        }

        if (roomTypeMappedMap.containsKey(mRoomTypeId)) {
            isMapped = roomTypeMappedMap.get(mRoomTypeId);
        } else {

            CtriposMapRoomtypeDO roomtypeDO = new CtriposMapRoomtypeDO();
            roomtypeDO.setMerchantCode(merchantCode);
            roomtypeDO.setShopId(Integer.valueOf(shopId));
            roomtypeDO.setHotelId(hotelId.intValue());
            roomtypeDO.setRoomTypeId(mRoomTypeId.intValue());
            List<CtriposMapRoomtypeDO> roomtypeDOS = roomtypeMapper.select(roomtypeDO);

            isMapped = !CollectionUtils.isEmpty(roomtypeDOS) && roomtypeDOS.get(0).getMapStatus() == 2;

            roomTypeMappedMap.put(mRoomTypeId, isMapped);
        }

        return isMapped;
    }

    /**
     * 酒店是否映射
     *
     * @param mHotelId
     * @param merchantCode
     * @param shopId
     * @param hotelMappedMap
     * @return
     */
    private boolean isHotelMapped(Long mHotelId, String merchantCode, String shopId, Map<Long, Boolean> hotelMappedMap) {

        boolean isMapped = false;

        if (null == mHotelId || StringUtils.isBlank(merchantCode) || StringUtils.isBlank(shopId)) {
            log.error("判断酒店映射时，酒店或商家编码或店铺为空。mHotelId={},merchantCode={},shopId={}", String.valueOf(mHotelId), merchantCode, shopId);
            return isMapped;
        }

        //先从缓存中取，没有再查库
        if (hotelMappedMap.containsKey(mHotelId)) {
            isMapped = hotelMappedMap.get(mHotelId);
        } else {

            CtriposMapHotelDO hotelDO = new CtriposMapHotelDO();
            hotelDO.setMerchantCode(merchantCode);
            hotelDO.setShopId(Integer.valueOf(shopId));
            hotelDO.setHotelId(mHotelId.intValue());
            List<CtriposMapHotelDO> hotelDOS = hotelMapper.select(hotelDO);

            isMapped = !CollectionUtils.isEmpty(hotelDOS) && hotelDOS.get(0).getMapStatus() == 2;

            //结果缓存起来
            hotelMappedMap.put(mHotelId, isMapped);
        }
        return isMapped;
    }


    /**
     * 构建默认的携程价格对象
     *
     * @param saleDate
     * @param currencyCode
     * @return
     */
    private ArrayOfRateAmountMessageTypeRate.Rate buildDefaultRate(Date saleDate, String currencyCode) {
        ArrayOfRateAmountMessageTypeRate.Rate rate = new ArrayOfRateAmountMessageTypeRate.Rate();
        rate.setStart(DateUtil.dateToString(saleDate));
        rate.setEnd(DateUtil.dateToString(saleDate));

        ArrayOfRateUploadTypeBaseByGuestAmt baseByGuestAmts = new ArrayOfRateUploadTypeBaseByGuestAmt();
        ArrayOfRateUploadTypeBaseByGuestAmt.BaseByGuestAmt baseByGuestAmt = new ArrayOfRateUploadTypeBaseByGuestAmt.BaseByGuestAmt();
        baseByGuestAmt.setCode(CtripOSCommonConstants.RATE_BASE_BY_GUEST_AMT_CODE);
        baseByGuestAmt.setAmountAfterTax(new BigDecimal(0d));
        baseByGuestAmt.setAmountBeforeTax(new BigDecimal(0d));
        baseByGuestAmt.setCurrencyCode(currencyCode);

        List<ArrayOfRateUploadTypeBaseByGuestAmt.BaseByGuestAmt> baseByGuestAmtList = new ArrayList<>();
        baseByGuestAmtList.add(baseByGuestAmt);
        baseByGuestAmts.getBaseByGuestAmt().addAll(baseByGuestAmtList);
        rate.setBaseByGuestAmts(baseByGuestAmts);

        RateUploadType.MealsIncluded mealsIncluded = new RateUploadType.MealsIncluded();
        mealsIncluded.setBreakfast(false);
        rate.setMealsIncluded(mealsIncluded);

        return rate;
    }

    /**
     * 构建默认的携程连住条款对象
     *
     * @param commodityId
     * @param roomTypeId
     * @param saleDate
     * @return
     */
    private AvailStatusMessageType buildCommonRoomStatus(Long commodityId, Long roomTypeId, Date saleDate) {
        AvailStatusMessageType availStatusMessageType = new AvailStatusMessageType();
        StatusApplicationControlType statusApplicationControlType = new StatusApplicationControlType();
        statusApplicationControlType.setStart(DateUtil.dateToString(saleDate));
        statusApplicationControlType.setEnd(DateUtil.dateToString(saleDate));
        statusApplicationControlType.setRatePlanCategory(CtripOSCommonConstants.PAY_METHOD_PREPAY);
        statusApplicationControlType.setRatePlanCode(String.valueOf(commodityId));
        statusApplicationControlType.setInvTypeCode(String.valueOf(roomTypeId));
        availStatusMessageType.setStatusApplicationControl(statusApplicationControlType);
        return availStatusMessageType;
    }

    /**
     * 构建默认的携程房态对象
     *
     * @param commodityId
     * @param roomTypeId
     * @param saleDate
     * @return
     */
    private AvailStatusMessageType buildDefaultAvailStatusMessageType(Long commodityId, Long roomTypeId, Date saleDate) {
        AvailStatusMessageType availStatusMessageType = buildCommonRoomStatus(commodityId, roomTypeId, saleDate);


        AvailStatusMessageType.RestrictionStatus restrictionStatus = new AvailStatusMessageType.RestrictionStatus();
        //这里默认推Master
        restrictionStatus.setRestriction(RestrictionTypeEnum.MASTER.key);
        restrictionStatus.setStatus(AvailabilityStatusType.CLOSE);
        availStatusMessageType.setRestrictionStatus(restrictionStatus);

        availStatusMessageType.setBookingLimit(new BigInteger("0"));
        availStatusMessageType.setBookingLimitMessageType(CtripOSCommonConstants.INVENTORY_BOOKING_LIMIT_MESSAGE_TYPE);
        return availStatusMessageType;
    }

    /**
     * 构建认证的PosType对象
     *
     * @param shopInfoDO
     * @return
     */
    private POSType buildPosType(CtriposShopInfoDO shopInfoDO) {
        CompanyNameType company = new CompanyNameType();
        company.setCode(shopInfoDO.getCode());
        company.setCodeContext(shopInfoDO.getCodeContext());

        SourceType.RequestorID requestorId = new SourceType.RequestorID();
        requestorId.setID(shopInfoDO.getApiId());
        requestorId.setType(shopInfoDO.getType());
        requestorId.setMessagePassword(shopInfoDO.getMessagePassword());
        requestorId.setCompanyName(company);

        SourceType source = new SourceType();
        source.setRequestorID(requestorId);
        final POSType posType = new POSType();
        posType.setSource(source);
        return posType;
    }
}
